/*
* Copyright (c) 2006-2007 Erin Catto http://www.box2d.org
*
* This software is provided 'as-is', without any express or implied
* warranty.  In no event will the authors be held liable for any damages
* arising from the use of this software.
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely, subject to the following restrictions:
* 1. The origin of this software must not be misrepresented; you must not
* claim that you wrote the original software. If you use this software
* in a product, an acknowledgment in the product documentation would be
* appreciated but is not required.
* 2. Altered source versions must be plainly marked as such, and must not be
* misrepresented as being the original software.
* 3. This notice may not be removed or altered from any source distribution.
*/

#include <Box2D/Box2D.h>

#include <stdio.h>

#include "SDL.h"
#include "../model/Figure.h"
#include "../model/Pentagon.h"

//Using SDL, standard IO, and strings
#include <SDL.h>
#include <stdio.h>
#include <string>
#include <iostream>
#include <fstream>
#include "SDL_image.h"
#include <Box2D/Dynamics/B2Fixture.h>;
#include "../view/PentagonView.h"


using namespace std;

//Screen dimension constants
const int SCREEN_WIDTH = 800;
const int SCREEN_HEIGHT = 600;
const int SCALE = 1.0;

//Starts up SDL and creates window
bool init();

//Loads media
bool loadMedia();

//Frees media and shuts down SDL
void close();

//Loads individual image
SDL_Surface* loadSurface(std::string path);

//The window we'll be rendering to
SDL_Window* gWindow = NULL;

//The surface contained by the window
SDL_Surface* gScreenSurface = NULL;

//Current displayed image
SDL_Surface* gStretchedSurface = NULL;

SDL_Renderer* gRenderer = NULL;

bool init()
{
	//Initialization flag
	bool success = true;

	//Initialize SDL
	if (SDL_Init(SDL_INIT_VIDEO) < 0)
	{
		printf("SDL could not initialize! SDL Error: %s\n", SDL_GetError());
		success = false;
	}
	else
	{
		//Create window
		gWindow = SDL_CreateWindow("Estirando la mascara", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, SCREEN_WIDTH, SCREEN_HEIGHT, SDL_WINDOW_SHOWN);
		if (gWindow == NULL)
		{
			printf("Window could not be created! SDL Error: %s\n", SDL_GetError());
			success = false;
		}
		else
		{
			//Get window surface
			gScreenSurface = SDL_GetWindowSurface(gWindow);
		}
	}

	return success;
}

bool loadMedia()
{
	//Loading success flag
	bool success = true;

	//Load stretching surface
	gStretchedSurface = loadSurface("./config/defaultMask.png");
	if (gStretchedSurface == NULL)
	{
		printf("Failed to load stretching image!\n");
		success = false;
	}

	return success;
}

void close()
{
	//Free loaded image
	SDL_FreeSurface(gStretchedSurface);
	gStretchedSurface = NULL;

	//Destroy window
	SDL_DestroyWindow(gWindow);
	gWindow = NULL;

	//Quit SDL subsystems
	SDL_Quit();
}

SDL_Surface* loadSurface(string path)
{

	ofstream fp;
	fp.open("./tests/loadError.txt");


	//The final optimized image
	SDL_Surface* optimizedSurface = NULL;

	//Load image at specified path
	SDL_Surface* loadedSurface = IMG_Load(path.c_str());
	if (loadedSurface == NULL)
	{
		fp << "Unable to load image " << path.c_str() << " SDL Error: " << SDL_GetError() << endl;
	}
	else
	{
		//Convert surface to screen format
		optimizedSurface = SDL_ConvertSurface(loadedSurface, gScreenSurface->format, NULL);
		if (optimizedSurface == NULL)
		{
			fp << "Unable to optimize img " << path.c_str() << " SDL Error: " << SDL_GetError() << endl;
		}

		//Get rid of old loaded surface
		SDL_FreeSurface(loadedSurface);
	}
	fp.close();
	return optimizedSurface;
}

Position* transformCoordSDLToBox2D(Position* position) {
	Position* newPosition = new Position();

	newPosition->setX(float32(position->getX())/SCALE);
	newPosition->setY(float32(SCREEN_HEIGHT - position->getY())/SCALE);

	return newPosition;
}


// This is a simple example of building and running a simulation
// using Box2D. Here we create a large ground box and a small dynamic
// box.
// There are no graphics for this example. Box2D is meant to be used
// with your rendering engine in your game engine.
int main(int argc, char** argv)
{
	if (!init()) {
		return 1;
	}

	if (!loadMedia()){
		return 2;
	}

	// Cargar mascara escalada
	// Es muy importante cargar la mascara con BlitScaled para ajustar el tamanio de la misma al de la SDL_Window
	SDL_Rect stretchRect;
	stretchRect.x = 0;
	stretchRect.y = 0;
	stretchRect.w = SCREEN_WIDTH;
	stretchRect.h = SCREEN_HEIGHT;
	SDL_BlitScaled(gStretchedSurface, NULL, gScreenSurface, &stretchRect);

	
	B2_NOT_USED(argc);
	B2_NOT_USED(argv);

	// Define the gravity vector.
	b2Vec2 gravity(0.0f, -100.0f);

	// Construct a world object, which will hold and simulate the rigid bodies.
	b2World world(gravity);



	// Define the ground body.
	b2BodyDef groundBodyDef;
	groundBodyDef.position.Set(0.0f, -10.0f);

	// Call the body factory which allocates memory for the ground body
	// from a pool and creates the ground box shape (also from a pool).
	// The body is also added to the world.
	b2Body* groundBody = world.CreateBody(&groundBodyDef);

	// Define the ground box shape.
	b2PolygonShape groundBox;

	// The extents are the half-widths of the box.
	groundBox.SetAsBox(50.0f, 10.0f);

	// Add the ground fixture to the ground body.
	groundBody->CreateFixture(&groundBox, 0.0f);

	/* INICIO DE CREACION DE PENTAGONO */

	Pentagon* pentagon = new Pentagon();
	pentagon->setRGBColor(10, 50, 255);
	pentagon->setMass(1.0f);
	pentagon->setRotation(0.0f);
	pentagon->setScale(10);
	pentagon->set_position(10.0f, 10.0f);

	pentagon->initPoints();

	b2BodyDef bodyDef;
	bodyDef.type = b2_dynamicBody;
	bodyDef.position.Set(200.0f, 500.0f);

	pentagon->setFigureBody(world.CreateBody(&bodyDef));

	//Cambio las coord de SDL a Box2D
	Position* box2DPosition = transformCoordSDLToBox2D(new Position(pentagon->getX_position(), pentagon->getY_position()));
		
	b2Vec2* points = new b2Vec2[pentagon->getType()];

	// Seteo los vertices de la figura en el vector puntos para cargar
	// este al poligono
	for (std::vector<Position*>::size_type i = 0; i != pentagon->getPoints().size(); i++) {
		//Cambio las coord de SDL a Box2D
		Position* box2DPositionVec = transformCoordSDLToBox2D(pentagon->getPoints()[i]);
		points[i].Set(box2DPositionVec->getX(), box2DPositionVec->getY());
	}

	b2PolygonShape polygon;
	polygon.Set(points, pentagon->getType());

	// Define the dynamic body fixture.
	b2FixtureDef fixtureDef;
	fixtureDef.shape = &polygon;

	// Set the box density to be non-zero, so it will be dynamic.
	fixtureDef.density = 50.0f;

	// Override the default friction.
	fixtureDef.friction = 0.1f;

	// Add the shape to the body.
	pentagon->getFigureBody()->CreateFixture(&polygon, 1.0f);


	// Prepare for simulation. Typically we use a time step of 1/60 of a
	// second (60Hz) and 10 iterations. This provides a high quality simulation
	// in most game scenarios.
	float32 timeStep = 1.0f / 60.0f;
	int32 velocityIterations = 6;
	int32 positionIterations = 2;

	PentagonView* pentagonView = new PentagonView(pentagon, 1);
	pentagonView->setPixelWindowWidth(SCREEN_WIDTH);
	pentagonView->setPixelWindowHeight(SCREEN_HEIGHT);
	pentagonView->setWindow(gWindow);
	pentagonView->setRenderer(gRenderer);
	pentagonView->setScreenSurface(gScreenSurface);

	ofstream fp;
	fp.open("caidaPentagono.txt");

	for (int32 i = 0; i < 200; ++i)
	{
		world.Step(timeStep, velocityIterations, positionIterations);
				
		b2Vec2 position = pentagon->getFigureBody()->GetPosition();
		fp << "( " << position.x << " , " << position.y << " )" << endl;
		
		pentagon->set_position(position.x, position.y);
		pentagon->initPoints();
		
		SDL_RenderPresent(gRenderer);
		pentagonView->notify();

		SDL_Delay(100);
	}
	fp.close();

	// When the world destructor is called, all bodies and joints are freed. This can
	// create orphaned pointers, so be careful about your world management.

	return 0;
}
